package model

import (
    "encoding/json"
    "errors"
    "fmt"

    "git.inspur.com/x/models"

    adapter "git.inspur.com/x/iam-adapter-go"
    "git.inspur.com/x/iam-adapter-go/resource"
)

type FuncInfo struct {
    Id               string        `json:"functionId" valid:"-" xorm:"'id' pk"`
    IdRepeat         string        `json:"id" valid:"-" xorm:"-"` // 冗余字段，为 iam 策略校验使用
    GroupDisplayName string        `json:"groupDisplayName" valid:"-" xorm:"'display_name' <-"`
    GroupId          string        `json:"groupId" valid:"-"`
    GroupName        string        `json:"groupName" valid:"-"`
    Creator          string        `json:"creator" valid:"-"`
    A5tId            string        `xorm:"account_id" json:"accountId" valid:"-"`
    CreatorName      string        `json:"creatorName" valid:"-"`
    A5tName          string        `xorm:"account_name" json:"accountName" valid:"-"`
    FunctionName     string        `json:"functionName" valid:"length(2|50)~valid.func.name_length,matches(^(([a-z0-9][a-z0-9-]*)?[a-z0-9])?$)~valid.func.name_match" xorm:"'name'"`
    Runtime          string        `json:"runtime" valid:"runtime~valid.func.runtime"`
    RegionCode       string        `json:"regionCode" valid:"-"`
    RegionName       string        `json:"regionName" valid:"-"`
    CodeInfo         CodeInfoExt   `json:"codeInfo" valid:"-"  xorm:"-"`
    Status           string        `json:"status" valid:"-"`
    Checkpoint       string        `json:"checkpoint" valid:"-"`
    MaxVersion       int           `json:"maxVersion" valid:"-"`
    GpuNum           int           `json:"gpuNum" valid:"-" xorm:"<-"`
    GpuType          string        `json:"gpuType" valid:"-" xorm:"<-"`
    DeleteTime       models.Time   `xorm:"deleted" json:"deleteTime" valid:"-"`
    CreateTime       models.Time   `xorm:"created" json:"createTime" valid:"-"`
    UpdateTime       models.Time   `xorm:"updated" json:"updateTime" valid:"-"`
    Description      string        `json:"description" valid:"length(0|2000)~valid.func.desc,optional" xorm:"<-"` // go语言中一个汉字占3个字节
    MemorySize       int           `json:"memorySize" valid:"memory~valid.func.mem" xorm:"<-"`
    Timeout          int           `json:"timeout" valid:"range(3|600)~valid.func.timeout" xorm:"<-"`
    Handler          string        `json:"handler" valid:"length(1|255)~valid.func.handler" xorm:"<-"`
    Concurrency      int           `json:"concurrency" valid:"range(1|20)~valid.func.concurrency" xorm:"<-"`
    Envs             []Environment `json:"environment" valid:"-" xorm:"json <- 'envs'"`
    EnableVpc        int           `json:"enableVpc" valid:"range(0|1)~valid.func.vpc" xorm:"<- 'is_enable_vpc'"`
    VpcInfo          VpcInfo       `json:"vpcInfo" valid:"-" xorm:"json <-"`
    VersionId        string        `json:"versionId" valid:"-" xorm:"-"`
    VersionName      string        `json:"versionName" valid:"-" xorm:"-"`
    ReserveNumber    int           `json:"reserveNumber" valid:"range(0|5)~valid.func.reserve" xorm:"<-"`
    EnablePublic     int           `json:"enablePublic" valid:"range(0|1)~valid.func.public" xorm:"<- 'is_enable_public'"`

    ContainerImage   string `json:"containerImage" valid:"-" xorm:"json <-"`
    ContainerCommand string `json:"containerCommand" valid:"-" xorm:"json <-"`
    ContainerArgs    string `json:"containerArgs" valid:"-" xorm:"json <-"`
    ContainerPort    string `json:"containerPort" valid:"custom-container-port~valid.func.port" xorm:"json <-"`

    InitHandler   string   `json:"initializeHandler" valid:"length(0|255)~valid.func.init,optional" xorm:"<-"`
    InitTimeout   int      `json:"initializeTimeout" valid:"range(0|300)~valid.func.init_timeout,optional" xorm:"<-"`
    BindingLayers []string `json:"bindingLayers" valid:"-" xorm:"json <-"`
    LayerInfos    []*Layer `json:"layerInfos" valid:"-" xorm:"-"`
    LayerPath     []string `json:"-" valid:"-" xorm:"-"`
}

type LayerInfo struct {
    BindingLayers []string `json:"bindingLayers" valid:"-" xorm:"-"`
}

type UrlInfo struct {
    FuncId     string `json:"funcId" valid:"-" `
    FuncName   string `json:"funcName" valid:"-"`
    GroupId    string `json:"groupId" valid:"-"`
    GroupName  string `json:"groupName" valid:"-"`
    TargetId   string `json:"targetId" valid:"-"`
    TargetName string `json:"targetName" valid:"-"`
}

type FuncCopyInfo struct {
    Description        string `json:"description" valid:"-"`
    TargetGroupId      string `json:"targetGroupId" valid:"-"`
    NewFunctionName    string `json:"newFunctionName" valid:"-"`
    IsCopyConfig       bool   `json:"isCopyConfig" valid:"-"`
    SourceFunctionName string `valid:"-"`
    SourceGroupName    string `valid:"-"`
    TragetGroupName    string `valid:"-"`
}

func (f FuncInfo) TableName() string {
    return "fcs_func"
}

func CreateFunction(info *FuncInfo) (*FuncInfo, error) {

    if _, err := models.Engine.Insert(info); err != nil {
        _ = logger.Error("CreateFunction: insert error %v", err)
        return nil, err
    }
    return info, nil
}

func CheckFuncExist(groupId, funcName, userId string) (bool, error) {
    f := &FuncInfo{}
    return models.Engine.Cols("id").Where("account_id=?", userId).And("group_id=?", groupId).And("name=?", funcName).Exist(f)
}

func GetFuncInfoByGroup(groupId, funcId string, user *resource.User) (*FuncInfo, error) {
    funcInfo := new(FuncInfo)
    // 由于funcInfo结构体中的实际字段数量比数据库表要多，直接用结构体查询会有问题，所以为了兼容，这里把属性单独列出来
    has, err := models.Engine.Cols("id", "creator", "creator_name", "account_id", "account_name", "group_id", "group_name",
        "name", "runtime", "checkpoint", "region_code", "region_name", "status", "max_version", "create_time", "update_time", "delete_time").
        Where("id=?", funcId).And("group_id=?", groupId).And("account_id=?", user.AccountId).Get(funcInfo)
    if err != nil {
        _ = logger.Error("GetFuncInfoByGroup: get funcInfo error %v", err)
        return nil, err
    }
    if !has {
        _ = logger.Warn("GetFuncInfoByGroup: did not find func_info, groupId=%v, funcId=%v, userId=%v", groupId, funcId, user.Id)
        return nil, errors.New("get func_info by group failed")
    }
    // 冗余字段，为 iam 策略校验使用
    funcInfo.IdRepeat = funcInfo.Id
    return funcInfo, nil
}

func GetAllFuncInfoByGroup(groupId, funcId string, user *resource.User) (*FuncInfo, error) {
    funcInfo := new(FuncInfo)
    has, err := models.Engine.Cols("id", "creator", "creator_name", "account_id", "account_name", "group_id", "group_name",
        "name", "runtime", "region_code", "region_name", "status", "max_version", "create_time", "update_time", "delete_time").
        Where("id=?", funcId).And("group_id=?", groupId).And("account_id=?", user.AccountId).Unscoped().Get(funcInfo)
    if err != nil {
        _ = logger.Error("GetAllFuncInfoByGroup: get funcInfo error %v", err)
        return nil, err
    }
    if !has {
        _ = logger.Warn("GetAllFuncInfoByGroup: did not find func_info, groupId=%v, funcId=%v, userId=%v", groupId, funcId, user.AccountId)
        return nil, errors.New("get func_info failed")
    }
    return funcInfo, nil
}

func GetFuncInfoListByGroup(query *FunctionListQuery) ([]*FuncInfo, error) {
    var fs []*FuncInfo
    session := models.Engine.Join("left", "fcs_func_version",
        "fcs_func.id = fcs_func_version.func_id AND fcs_func_version.`name`='"+FunctionLatestVersion+"'").
        Join("left", "fcs_group", "fcs_func.group_id = fcs_group.id").
        Where("fcs_func.account_id=?", query.AccountId).Desc("fcs_func.update_time")
    if query.GroupId != "" {
        session = session.Where("fcs_func.group_id=?", query.GroupId)
    }
    if query.FunctionName != "" {
        // 如果使用 like，查询字符串为%和_时候，会把所有数据查询出来
        session = session.Where("INSTR(fcs_func.name,?)", query.FunctionName)
    }
    if err := session.Find(&fs); err != nil {
        _ = logger.Error("GetFuncInfoListByGroup: get funcInfo list error %v", err)
        return nil, err
    }

    // 冗余字段，为 iam 策略校验使用
    for _, item := range fs {
        item.IdRepeat = item.Id
    }

    filteredList := adapter.FilterListData(fs).([]interface{})
    resultList := make([]*FuncInfo, 0)
    for _, f := range filteredList {
        f, ok := f.(*FuncInfo)
        if ok {
            resultList = append(resultList, f)
        }
    }

    return resultList, nil
}

func UpdateFuncInfo(funcInfo *FuncInfo) (*FuncInfo, error) {
    line, err := models.Engine.ID(funcInfo.Id).Update(funcInfo)
    if err != nil {
        _ = logger.Error("UpdateFuncInfo: update funcInfo error %v", err)
        return nil, err
    }
    if line == 0 {
        funcInfoJson, _ := json.Marshal(funcInfo)
        _ = logger.Warn("UpdateFuncInfo: did not update func_info, funcInfo=%v", string(funcInfoJson))
        return nil, errors.New("UpdateFuncInfo func_info failed")
    }
    return funcInfo, nil
}

func DeleteFuncInfo(funcInfo *FuncInfo) (*FuncInfo, error) {
    line, err := models.Engine.ID(funcInfo.Id).Delete(funcInfo)
    if err != nil {
        _ = logger.Error("DeleteFuncInfo: delete funcInfo error %v", err)
        return nil, err
    }
    if line == 0 {
        funcInfoJson, _ := json.Marshal(funcInfo)
        _ = logger.Warn("UpdateFuncInfo: did not delete func_info, funcInfo=%v", string(funcInfoJson))
        return nil, errors.New("delete func_info failed")
    }
    return funcInfo, nil
}

func UpdateFunctionStatus(funcInfo *FuncInfo) (*FuncInfo, error) {
    line, err := models.Engine.ID(funcInfo.Id).Cols("status").Update(funcInfo)
    if err != nil {
        _ = logger.Error("UpdateFunctionStatus: update func status error, %s", err.Error())
        return nil, err
    }
    if line == 0 {
        funcInfoJson, _ := json.Marshal(funcInfo)
        _ = logger.Warn("UpdateFunctionStatus: did not update func_info, funcInfo=%v", string(funcInfoJson))
        return nil, errors.New("UpdateFuncInfo func_info failed")
    }
    return funcInfo, nil
}

func GetAllFuncInfoList() ([]*FuncInfo, error) {
    funcInfoList := make([]*FuncInfo, 0)
    session := models.Engine.Join("left", "fcs_func_version", "fcs_func.id = fcs_func_version.func_id AND fcs_func_version.`name`='"+FunctionLatestVersion+"'").
        Desc("fcs_func.update_time")
    err := session.Find(&funcInfoList)
    if err != nil {
        _ = logger.Error("GetAllFuncInfoList: get funcInfo list error %v", err)
        return nil, err
    }
    return funcInfoList, nil
}

func GetTimeoutFuncInfo() ([]*FuncInfo, error) {
    funcInfoList := make([]*FuncInfo, 0)
    err := models.Engine.Cols("id", "creator", "creator_name", "account_id", "account_name", "group_id", "group_name",
        "name", "runtime", "region_code", "region_name", "status", "max_version", "create_time", "update_time", "delete_time").
        Where("status=?", FunctionUpdating).Or("status=?", FunctionCreating).
        Where(fmt.Sprintf("update_time <= DATE_SUB(NOW(), INTERVAL %d SECOND)", Base.KsvcWaitingTime+60)).
        Find(&funcInfoList)
    return funcInfoList, err
}

func GetFuncInfoById(funcId string) (*FuncInfo, error) {
    funcInfo := new(FuncInfo)
    // 由于funcInfo结构体中的实际字段数量比数据库表要多，直接用结构体查询会有问题，所以为了兼容，这里把属性单独列出来
    has, err := models.Engine.Cols("id", "creator", "creator_name", "account_id", "account_name", "group_id", "group_name",
        "name", "runtime", "region_code", "region_name", "status", "max_version", "create_time", "update_time", "delete_time").
        Where("id=?", funcId).Get(funcInfo)
    if err != nil {
        _ = logger.Error("GetFuncInfoById: get funcInfo error, %s", err.Error())
        return nil, err
    }
    if !has {
        _ = logger.Warn("GetFuncInfoById: did not find func_info, funcId=%v", funcId)
        return nil, errors.New("get func_info by id failed")
    }
    return funcInfo, nil
}
